home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / a_utils / _archvrs / unix / freeze-2.lha / freeze / freeze.c < prev    next >
C/C++ Source or Header  |  1993-02-22  |  25KB  |  1,023 lines

  1. #include "freeze.h"
  2. #include "lz.h"
  3. #include "bitio.h"
  4. #include "patchlev.h"
  5.  
  6. /*
  7.  * Freeze - data freezing program
  8.  * Version 1.0:
  9.  * This program is made from GNU compress.c and Yoshizaki/Tagawa's
  10.  * lzhuf.c. (Thanks to all of them.)
  11.  * The algorithm is modified for using in pipe
  12.  * (added ENDOF symbol in Huffman table).
  13.  * Version 1.1:
  14.  * Check for lack of bytes in frozen file when melting.
  15.  * Put the GetBit routine into DecodeChar for reduce function-
  16.  * call overhead when melting.
  17.  * Version 1.2:
  18.  * Added delayed coding a la COMIC.
  19.  * Now freeze works on Intels (*NIX, Microsoft, Turbo),
  20.  * Sun (SunOS).
  21.  * Version 2.0:
  22.  * Buffer size is now 8192 bytes, maximum match length - 256 bytes.
  23.  * Improved hash function (with tuning of hash-table)
  24.  * Version 2.1: Noticeable speedup: Insert_Node and Get_Next_Match
  25.  * are now separated. (Boyer-Moore string matching)
  26.  * Version 2.2: Tunable static Huffman table for position information,
  27.  * this info may be given in the command string now.
  28.  * Version 2.2.3: Bug fixes, 10% freezing speedup.
  29.  * Version 2.3: Minor bug fixes (DOS filenames handling, backward
  30.  * compatibility feature improved, "bits" compression ratio display,
  31.  * preventive check for special files), speedups, more comments added.
  32.  * Version 2.3.1: Typedefs cleaned, utime bug on the m88k corrected
  33.  * (pa@verano.sba.ca.us, clewis@ferret.ocunix.on.ca (Chris Lewis)),
  34.  * "chain threshold" heuristic used for speedup (in "greedy" mode) -
  35.  * a la ZIP (Jean-Loup Gailly). Max. hash bits reduced to 16.
  36.  * Version 2.3.2: Adaptation to TOS 1.04 (fifi@hiss.han.de), UTIMES
  37.  * handling (jik@athena.mit.edu).
  38.  * Version 2.3.3: More accurate adaptation for XENIX 286.
  39.  * Version 2.3.4: Minor bug fixes, HP-UX (longnames w/o BSD) handling,
  40.  * greedy_threshold added.
  41.  * Version 2.3.5: Noticeable speedup (on RISCs, don't know for sure
  42.  * about CISCs).
  43.  * Version 2.4: Yet another speedup, many general changes...
  44.  * Version 2.4.1 (unofficial): A bug is corrected.
  45.  * Version 2.5: Speedup of melting, more thorough adaptation to
  46.  * 16-bits (and 64-bits?) processors.
  47.  */
  48.  
  49. static char ident[] = "@(#) freeze.c 2.5.%d %s leo@ipmce.su\n";
  50.  
  51. int     exit_stat = 0;
  52.  
  53. void 
  54. Usage()
  55. {
  56. #ifdef DEBUG
  57.  
  58. # ifdef DOS
  59.   fprintf(stderr, "Usage: freeze [-cdDfitvVg] [file | +type ...]\n");
  60. # else
  61.   fprintf(stderr, "Usage: freeze [-cdDfvVg] [file | +type ...]\n");
  62. # endif                /* DOS */
  63.  
  64. #else
  65.  
  66. # ifdef DOS
  67.   fprintf(stderr, "Usage: freeze [-cdfitvVg] [file | +type ...]\n");
  68. # else
  69.   fprintf(stderr, "Usage: freeze [-cdfvVg] [file | +type ...]\n");
  70. # endif                /* DOS */
  71.  
  72. #endif                /* DEBUG */
  73. }
  74.  
  75. void    (*meltfunc) ();        /* To call something for melting */
  76.  
  77. int     topipe = 0,             /* Write output on stdout, suppress messages */
  78.         precious = 1,        /* Don't unlink output file on interrupt */
  79.         quiet = 1,        /* Don't tell me about freezing */
  80.         do_melt = 0,        /* freeze means "freeze" */
  81.         greedy = 0,        /* GREEDY parsing */
  82.         force = 0;        /* "Force" flag */
  83.  
  84. char    ofname[MAXNAMLEN];
  85. static struct stat statbuf;    /* Used by 'main' and 'copystat' routines */
  86.  
  87. #ifdef DOS
  88. char   *last_sep();        /* last slash, backslash, or colon */
  89. char    tail[3];        /* 2nd and 3rd chars of file extension */
  90. # ifdef TEXT_DEFAULT
  91. unsigned        image = O_TEXT;
  92. # else
  93. unsigned        image = O_BINARY;
  94. # endif
  95. #else
  96. #  define last_sep(s) strrchr((s), '/')    /* Unix always uses slashes */
  97. #  ifndef DEFFILE
  98. #   define DEFFILE "/usr/local/lib/freeze.cnf"
  99. #  endif
  100. char    deffile[] = DEFFILE;
  101. #endif
  102.  
  103. #ifdef DEBUG
  104. int     debug = 0, verbose = 0;
  105. char   *pr_char();
  106. #endif                /* DEBUG */
  107.  
  108. #if defined(GATHER_STAT) || defined(DEBUG)
  109. long    refers_out = 0, symbols_out = 0;
  110. #endif
  111.  
  112. /* Do not sleep when freeze works :-) */
  113. long    indc_count, indc_threshold;
  114. off_t   file_length = 0;    /* initial length of file */
  115.  
  116. RETSIGTYPE      (*bgnd_flag)();
  117.  
  118. void    writeerr(), copystat(), version(), tune_table();
  119.  
  120. /*****************************************************************
  121.  *
  122.  * Usage: freeze [-cdfivV] [-t] [-g...] [-x...] [type] [file ...]
  123.  * Inputs:
  124.  *
  125.  *      -c:         Write output on stdout, don't remove original.
  126.  *
  127.  *      -d:         If given, melting is done instead.
  128.  *
  129.  *      -g:         Use "greedy" parsing (1.5% worse, 40% faster).
  130.  *                  (Means nothing when melting). May be repeated.
  131.  *
  132.  *      -f:         Forces output file to be generated, even if one already
  133.  *                  exists, and even if no space is saved by freezeing.
  134.  *                  If -f is not used, the user will be prompted if stdin is
  135.  *                  a tty, otherwise, the output file will not be overwritten.
  136.  *
  137.  *      -i:         Image mode (defined only under MS-DOS).  Prevents
  138.  *                  conversion between UNIX text representation (LF line
  139.  *                  termination) in frozen form and MS-DOS text
  140.  *                  representation (CR-LF line termination) in melted
  141.  *                  form.  Useful with non-text files.  Default unless
  142.  *                  TEXT_DEFAULT specified.
  143.  *
  144.  *      -b:         Binary mode.  Synonym for -i.  MS-DOS only.
  145.  *
  146.  *      -t:         Text mode (defined only under MS-DOS).  Treat file
  147.  *                  as text (CR-LF and ^Z special) in melted form.  Default
  148.  *                  if TEXT_DEFAULT specified.
  149.  *
  150.  *      -g:         Greedy parsing - increases speed, decreases
  151.  *                  compression rate. Can be repeated.
  152.  *
  153.  *      -x:         "Anti- -g".
  154.  *
  155.  *      -v:         Write freezing statistics. -vv means "draw progress
  156.  *                  indicator".
  157.  *
  158.  *      -V:         Write version and compilation options.
  159.  *
  160.  *      file ...:   Files to be frozen.  If none specified, stdin
  161.  *            is used.
  162.  * Outputs:
  163.  *      file.F:     Frozen form of file with same mode, owner, and utimes
  164.  *    or stdout   (if stdin used as input)
  165.  *
  166.  * Assumptions:
  167.  *      When filenames are given, replaces with the frozen version
  168.  *      (.F suffix) only if the file decreases in size.
  169.  * Algorithm:
  170.  *      Modified Lempel-Ziv-SS method (LZSS), adaptive Huffman coding
  171.  *      for literal symbols and length info.
  172.  *      Static Huffman coding for position info. (Is it optimal ?)
  173.  *      Lower bits of position info are put in output
  174.  *      file without any coding because of their random distribution.
  175.  */
  176.  
  177. /* From compress.c. Replace .Z --> .F etc */
  178.  
  179. void 
  180. main(argc, argv)
  181. register int argc;
  182. char  **argv;
  183. {
  184.   short   overwrite = 0;    /* Do not overwrite unless given -f flag */
  185.   char    tempname[100];
  186.   char  **filelist, **fileptr;
  187.   char   *cp;
  188.  
  189. #ifdef TOS
  190.  
  191.   char   *argv0 = "freeze.ttp";    /* argv[0] is not defined :-( */
  192.  
  193. #endif
  194.  
  195. #ifndef DOS
  196.   char   *malloc();
  197. #endif
  198.  
  199.   extern RETSIGTYPE onintr();
  200.  
  201. #ifdef DOS
  202.   char   *sufp;
  203. #else
  204.   extern RETSIGTYPE oops();
  205. #endif
  206.  
  207. #ifndef DOS
  208.   if ((bgnd_flag = signal(SIGINT, SIG_IGN)) != SIG_IGN)
  209. #endif
  210.   {
  211.     (void) signal(SIGINT, onintr);
  212. #ifdef __TURBOC__
  213.  
  214. #ifndef TOS
  215.     setcbrk(1);
  216.  
  217. #endif
  218. #endif
  219. #ifndef DOS
  220.     (void) signal(SIGSEGV, oops);
  221. #endif
  222.   }
  223.   filelist = fileptr = (char **) (malloc(argc * sizeof(*argv)));
  224.   *filelist = NULL;
  225.  
  226.   if ((cp = last_sep(argv[0])) != 0) {
  227.     cp++;
  228.   } else {
  229.  
  230. #ifdef TOS
  231.  
  232.     cp = argv0;
  233.  
  234. #else
  235.     cp = argv[0];
  236.  
  237. #endif
  238.   }
  239.  
  240. #ifdef DOS
  241. /* use case-insensitive match: parent may not be command.com */
  242. #ifdef MSDOS
  243.   if (!stricmp(cp, "unfreeze.exe") || !stricmp(cp, "melt.exe")) {
  244. #else                /* TOS */
  245.   if (!stricmp(cp, "unfreeze.ttp") || !stricmp(cp, "melt.ttp")) {
  246. #endif
  247. #else
  248.   if (!strcmp(cp, "unfreeze") || !strcmp(cp, "melt")) {
  249. #endif
  250.  
  251.     do_melt = 1;
  252.  
  253. #ifdef DOS
  254. #ifdef MSDOS
  255.   } else if (stricmp(cp, "fcat.exe") == 0) {
  256. #else                /* TOS */
  257.   } else if (stricmp(cp, "fcat.ttp") == 0) {
  258. #endif
  259. #else
  260.   } else if (strcmp(cp, "fcat") == 0) {
  261. #endif
  262.  
  263.     do_melt = 1;
  264.     topipe = 1;
  265.  
  266.   } else {
  267.   /* Freezing */
  268.  
  269. #ifndef DOS
  270.     (void) defopen(deffile);
  271. #else
  272.     cp = strrchr(cp, '.');
  273.     *++cp = 'C';
  274.     *++cp = 'N';
  275.     *++cp = 'F';
  276.     *++cp = '\0';
  277.  
  278. #ifdef TOS
  279.     (void) defopen(argv0);
  280. #else
  281.     (void) defopen(argv[0]);
  282. #endif                /* TOS */
  283. #endif                /* DOS */
  284.  
  285.   }
  286. #ifdef HAVE_SETLINEBUF
  287.  /* 4.2BSD dependent - take it out if not */
  288.   setlinebuf(stderr);
  289. #endif                /* BSD */
  290.  
  291.     /* Argument Processing
  292.      * All flags are optional.
  293.      * -D => debug
  294.      * -V => print Version; debug verbose
  295.      * -d => do_melt
  296.      * -v => unquiet
  297.      * -g => greedy
  298.      * -f => force overwrite of output file
  299.      * -c => cat all output to stdout
  300.      * if a string is left, must be an input filename.
  301.      */
  302.  
  303.   for (argc--, argv++; argc > 0; argc--, argv++) {
  304.     if (**argv == '-' && (*argv)[1] != '-') {        /* A flag argument */
  305.       while (*++(*argv)) {    /* Process all flags in this arg */
  306.     switch (**argv) {
  307. #ifdef DEBUG
  308.     case 'D':
  309.       debug = 1;
  310.       break;
  311.     case 'V':
  312.       verbose = 1;
  313. #else
  314.     case 'V':
  315.       version();
  316. #endif                /* DEBUG */
  317.       break;
  318. #ifdef DOS
  319.     case 'i':
  320.     case 'b':
  321.       image = O_BINARY;    /* binary (aka image) mode */
  322.       break;
  323.  
  324.     case 't':        /* text mode */
  325.       image = O_TEXT;
  326.       break;
  327. #endif
  328.     case 'v':
  329.       quiet--;
  330.       break;
  331.     case 'g':
  332.       greedy++;
  333.       break;
  334.     case 'x':
  335.       greedy = -1;
  336.       break;
  337.     case 'd':
  338.       do_melt = 1;
  339.       break;
  340.     case 'f':
  341.     case 'F':
  342.       overwrite = 1;
  343.       force = 1;
  344.       break;
  345.     case 'c':
  346.       topipe = 1;
  347.       break;
  348.     case 'q':
  349.       quiet = 1;
  350.       break;
  351.     default:
  352.       fprintf(stderr, "Unknown flag: '%c'; ", **argv);
  353.       Usage();
  354.       exit(1);
  355.     }
  356.       }
  357.     } else {            /* Input file name */
  358.       *fileptr++ = *argv;    /* Build input file list */
  359.       *fileptr = NULL;
  360.     }
  361.   }
  362.  
  363. #ifdef DEBUG
  364.   if (verbose && !debug)
  365.     version();
  366. #endif
  367.  
  368.   if (do_melt) {
  369.     /* use all otherwise unused memory for I/O buffers */
  370.     setvbuf(stdin, (char*) next, _IOFBF, (size_t) WINSIZE * sizeof(hash_t));
  371.     setvbuf(stdout, (char*) hashtab, _IOFBF, (size_t) hash_size * sizeof(hash_t));
  372.   } else {
  373.     /* try to allocate all left memory for I/O buffers */
  374.     size_t i = 64;      /* 512-byte blocks */
  375. #ifdef SEGMENTED
  376.     char * p;
  377.     do {
  378.       if ((p = malloc(i << 9)) != NULL)
  379.     break;
  380.       i--;
  381.     } while (i >= 4);
  382.     free(p);
  383.     if (quiet < -1) fprintf(stderr, "%d blocks allocated\n", i);
  384. #endif
  385.     /* Don't check for failure - things cannot get worse than they were */
  386.     setvbuf(stdin, NULL, _IOFBF, (i / 2) << 9);
  387.     setvbuf(stdout, NULL, _IOFBF, (i - i / 2) << 9);
  388.   }
  389.  
  390.   if (*filelist != NULL) {
  391.     for (fileptr = filelist; *fileptr; fileptr++) {
  392.       if ((**fileptr == '+' || **fileptr == '-') && do_melt == 0) {
  393.     tune_table(*fileptr + 1 + (**fileptr == '-'));
  394.     /* If a file type is given and no file names, a pipe
  395.      * operation is requested, but
  396.      * we will ignore an idiotic case of setting an resetting
  397.      * tables without any filename.
  398.      */
  399.     if (filelist[1] == NULL)
  400.       goto Pipe;
  401.     continue;
  402.       }
  403.       exit_stat = 0;
  404.       if (do_melt != 0) {    /* MELTING */
  405.  
  406. #ifdef DOS
  407.       /* Check for .F or XF suffix; add one if necessary */
  408.     cp = *fileptr + strlen(*fileptr) - 2;
  409.     tail[0] = '\0';
  410.     if ((*cp != '.' && *cp != 'X' && *cp != 'x') ||
  411.       (*(++cp) != 'F' && *cp != 'f')) {
  412.       (void) strcpy(tempname, *fileptr);
  413.       if ((cp = strrchr(tempname, '.')) == NULL)
  414.         (void) strcat(tempname, ".F");
  415.       else if (*(++cp) == '\0')
  416.       /* pseudo-extension: FOOBAR. */
  417.         (void) strcat(tempname, "F");
  418.       else {
  419.       /* cp now points to file extension */
  420.         tail[0] = cp[1];    /* save two chars */
  421.         tail[1] = cp[2];
  422.         tail[2] = '\0';
  423.         *(++cp) = '\0';
  424.         (void) strcat(tempname, "XF");
  425.       }
  426.       *fileptr = tempname;
  427.     }
  428. #else
  429.       /* Check for .F suffix */
  430.     if (strcmp(*fileptr + strlen(*fileptr) - 2, ".F") != 0) {
  431.     /* No .F: tack one on */
  432.       (void) strcpy(tempname, *fileptr);
  433.       (void) strcat(tempname, ".F");
  434.       *fileptr = tempname;
  435.     }
  436. #endif                /* DOS */
  437.  
  438.       /* Open input file for melting */
  439.  
  440.     if (checkstat(*fileptr))
  441.       continue;
  442.  
  443. #ifdef DOS
  444.     if ((freopen(*fileptr, "rb", stdin)) == NULL)
  445. #else
  446.     if ((freopen(*fileptr, "r", stdin)) == NULL)
  447. #endif
  448.     {
  449.       perror(*fileptr);
  450.       continue;
  451.     }
  452.       /* Check the magic number */
  453.     if (getchar() != MAGIC1)
  454.       goto reject;
  455.     switch (getchar()) {
  456. #ifdef COMPAT
  457.     case MAGIC2_1:
  458.       meltfunc = melt1;
  459.       break;
  460. #endif
  461.     case MAGIC2_2:
  462.       meltfunc = melt2;
  463.       break;
  464.     default:
  465.       reject:
  466.       fprintf(stderr, "%s: not in frozen format\n",
  467.         *fileptr);
  468.       continue;
  469.     }
  470.  
  471.       /* Generate output filename */
  472.     precious = 1;
  473.     (void) strcpy(ofname, *fileptr);
  474.     ofname[strlen(ofname) - 2] = '\0';    /* Strip off .F */
  475. #ifdef DOS
  476.     (void) strcat(ofname, tail);
  477. #endif
  478.       } else {
  479.  
  480.       /* FREEZING */
  481. #ifdef DOS
  482.     cp = *fileptr + strlen(*fileptr) - 2;
  483.     if ((*cp == '.' || *cp == 'X' || *cp == 'x') &&
  484.       (*(++cp) == 'F' || *cp == 'f')) {
  485.       fprintf(stderr, "%s: already has %s suffix -- no change\n",
  486.         *fileptr, --cp);    /* } */
  487. #else
  488.     if (strcmp(*fileptr + strlen(*fileptr) - 2, ".F") == 0) {
  489.       fprintf(stderr, "%s: already has .F suffix -- no change\n",
  490.         *fileptr);
  491. #endif                /* DOS */
  492.  
  493.       continue;
  494.     }
  495.       /* Open input file for freezing */
  496.  
  497.     if (checkstat(*fileptr))
  498.       continue;
  499.  
  500. #ifdef DOS
  501.     if ((freopen(*fileptr, image == O_TEXT ? "rt" : "rb", stdin))
  502.       == NULL)
  503. #else
  504.     if ((freopen(*fileptr, "r", stdin)) == NULL)
  505. #endif
  506.     {
  507.       perror(*fileptr);
  508.       continue;
  509.     }
  510.       /* Generate output filename */
  511.     (void) strcpy(ofname, *fileptr);
  512.  
  513. #ifndef HAVE_LONG_FILE_NAMES
  514.     if ((cp = last_sep(ofname)) != NULL)
  515.       cp++;
  516.     else
  517.       cp = ofname;
  518. # ifdef DOS
  519.     if (topipe == 0 && (sufp = strrchr(cp, '.')) != NULL &&
  520.       strlen(sufp) > 2)
  521.       fprintf(stderr,
  522.         "%s: part of filename extension will be replaced by XF\n",
  523.         cp);
  524. # else
  525.     if (topipe == 0 && strlen(cp) > 12) {
  526.       fprintf(stderr, "%s: filename too long to tack on .F\n", cp);
  527.       continue;
  528.     }
  529. # endif                /* DOS */
  530. #endif                /* LONGNAMES */
  531.  
  532. #ifdef DOS
  533.       /* There is no difference between FOOBAR and FOOBAR. names */
  534.     if ((cp = strrchr(ofname, '.')) == NULL)
  535.       (void) strcat(ofname, ".F");
  536.     else if (cp[1] == '\0')
  537.     /* FOOBAR. case */
  538.       (void) strcat(ofname, "F");
  539.     else {
  540.       cp[2] = '\0';
  541.       (void) strcat(ofname, "XF");
  542.     }
  543. #else
  544.     (void) strcat(ofname, ".F");
  545. #endif                /* DOS */
  546.  
  547.       }
  548.     /* Check for overwrite of existing file */
  549.       if (overwrite == 0 && topipe == 0) {
  550.     if (stat(ofname, &statbuf) == 0) {
  551.       char    response[2];
  552.       response[0] = 'n';
  553.       fprintf(stderr, "%s already exists;", ofname);
  554. #ifndef DOS
  555.       if (foreground()) {
  556. #endif
  557.         fprintf(stderr,
  558.           " do you wish to overwrite %s (y or n)? ", ofname);
  559.         (void) fflush(stderr);
  560.         (void) read(2, response, 2);
  561.         while (response[1] != '\n') {
  562.           if (read(2, response + 1, 1) < 0) {    /* Ack! */
  563.         perror("stderr");
  564.         break;
  565.           }
  566.         }
  567. #ifndef DOS
  568.       }
  569. #endif
  570.       if (response[0] != 'y') {
  571.         fprintf(stderr, "\tnot overwritten\n");
  572.         continue;
  573.       }
  574.     }
  575.       }
  576.       if (topipe == 0) {    /* Open output file */
  577.  
  578. #ifdef DEBUG
  579.     if (do_melt == 0 || debug == 0) {
  580. #endif
  581. #ifdef DOS
  582.       if (freopen(ofname, do_melt && image == O_TEXT ? "wt" : "wb",
  583.           stdout) == NULL)
  584. #else
  585.       if (freopen(ofname, "w", stdout) == NULL)
  586. #endif
  587.       {
  588.         perror(ofname);
  589.         continue;
  590.       }
  591. #ifdef DEBUG
  592.     }
  593. #endif
  594.     precious = 0;
  595.     if (quiet != 1) {
  596.       fprintf(stderr, "%s:", *fileptr);
  597.       indc_count = 1024;
  598.     }
  599.       } else {            /* output is to stdout */
  600. #ifdef MSDOS
  601.       /* freeze output always binary; melt output is binary if image ==
  602.          O_BINARY */
  603.     if (do_melt == 0 || image == O_BINARY)
  604.       setmode(fileno(stdout), O_BINARY);
  605. #endif
  606.       }
  607.     /* Actually do the freezing/melting */
  608.       if (do_melt == 0)
  609.     freeze();
  610. #ifndef DEBUG
  611.       else
  612.     (*meltfunc) ();
  613. #else
  614.       else if (debug && verbose)
  615.     printcodes(meltfunc == (void (*) ()) melt2);
  616.       else
  617.     (*meltfunc) ();
  618. #endif                /* DEBUG */
  619.  
  620.     /* check output status, and close to make sure data is written */
  621.       if (ferror(stdout) || (!topipe && fclose(stdout) < 0))
  622.     writeerr();
  623.  
  624.       if (topipe == 0)
  625.     copystat(*fileptr);    /* Copy stats */
  626.       precious = 1;
  627.     }
  628.   } else {            /* Standard input */
  629. Pipe:
  630.     if (fstat(fileno(stdin), &statbuf)) {
  631.       perror("stdin");
  632.       exit(1);
  633.     }
  634.     file_length = ((statbuf.st_mode & S_IFMT) == S_IFREG) ?
  635.     statbuf.st_size : 0;
  636.  
  637.     indc_threshold = file_length / 100;
  638.     if (indc_threshold < 4096)
  639.       indc_threshold = 4096;
  640.     if (do_melt)
  641.       indc_threshold *= 3;
  642.  
  643.     topipe = 1;
  644.     if (do_melt == 0) {
  645. #ifdef MSDOS
  646.     /* freeze output always binary */
  647.     /* freeze input controlled by -i -t -b switches */
  648.       setmode(fileno(stdout), O_BINARY);
  649.       setmode(fileno(stdin), image);
  650. #endif
  651.       freeze();
  652.       if (quiet != 1)
  653.     putc('\n', stderr);
  654.     } else {
  655. #ifdef MSDOS
  656.     /* melt input always binary */
  657.     /* melt output to stdout binary if so requested */
  658.       setmode(fileno(stdin), O_BINARY);
  659.       setmode(fileno(stdout), image);
  660. #endif
  661.     /* Check the magic number */
  662.       if (getchar() != MAGIC1)
  663.     goto badstdin;
  664.       switch (getchar()) {
  665. #ifdef COMPAT
  666.       case MAGIC2_1:
  667.     meltfunc = melt1;
  668.     break;
  669. #endif
  670.       case MAGIC2_2:
  671.     meltfunc = melt2;
  672.     break;
  673.       default:
  674.     badstdin:
  675.     fprintf(stderr, "stdin: not in frozen format\n");
  676.     exit(1);
  677.       }
  678.  
  679. #ifndef DEBUG
  680.       (*meltfunc) ();
  681. #else
  682.       if (debug && verbose)
  683.     printcodes(meltfunc == (void (*) ()) melt2);
  684.       else
  685.     (*meltfunc) ();
  686. #endif                /* DEBUG */
  687.     }
  688.   }
  689.   exit(exit_stat);
  690.  /* NOTREACHED */
  691. }
  692.  
  693. long    in_count = 1;        /* length of input */
  694.  
  695. /* Calculates and prints the compression ratio w/o floating point OPs */
  696.  
  697. void 
  698. prratio(stream, was, is)
  699. FILE   *stream;
  700. long    was, is;
  701. {
  702.   register long q;        /* This works everywhere */
  703.  
  704.   if (!is)
  705.     is++;
  706.  
  707.   if (was > 214748L) {        /* 2147483647/10000 */
  708.     q = was / (is / 10000L);
  709.   } else {
  710.     q = 10000L * was / is;    /* Long calculations, though */
  711.   }
  712.   if (q < 0) {
  713.     putc('-', stream);
  714.     q = -q;
  715.   }
  716.   fprintf(stream, "%d.%02d%%", (int) (q / 100), (int) (q % 100));
  717. #ifdef GATHER_STAT
  718.   fprintf(stream, "(%ld / %ld)", was, is);
  719. #endif
  720. }
  721. /* Calculates and prints bits/byte compression ratio as above */
  722.  
  723. void 
  724. prbits(stream, was, is)
  725. FILE   *stream;
  726. long    was, is;
  727. {
  728.   register long q;
  729.  
  730.   if (!was)
  731.     was++;
  732.  
  733.   if (is > 2684354L) {        /* 2147483647/800 */
  734.     q = is / (was / 800L);
  735.   } else {
  736.     q = 800L * is / was;
  737.   }
  738.   fprintf(stream, " (%d.%02d bits)", (int) (q / 100), (int) (q % 100));
  739. }
  740. /* There was an error when reading or writing files */
  741.  
  742. void 
  743. writeerr()
  744. {
  745.   if (!topipe) {
  746.     perror(ofname);
  747.     (void) unlink(ofname);
  748.   }
  749.   exit(1);
  750. }
  751.  
  752. void 
  753. copystat(ifname)
  754. char   *ifname;
  755. {
  756. #ifdef __TURBOC__
  757.   struct ftime utimbuf;
  758. #else
  759. #ifdef UTIMES
  760.   struct timeval timep[2];
  761. #else
  762.   struct utimbuf timep;
  763. #endif
  764. #endif
  765.  
  766.   int     mode;
  767.  
  768. #ifdef MSDOS
  769.   if (_osmajor < 3)
  770.     freopen("CON", "at", stdout);
  771.   else                /* MS-DOS 2.xx bug */
  772. # endif
  773.  
  774.     (void) fclose(stdout);
  775.  
  776.   if (exit_stat == 2 && (!force)) {    /* No freezing: remove file.F */
  777.  
  778.     if (quiet != 1)
  779.       fprintf(stderr, "-- file unchanged\n");
  780.  
  781.   } else {            /* ***** Successful Freezing ***** */
  782.  
  783.     if (stat(ifname, &statbuf)) {    /* file disappeared ?! */
  784.       perror(ifname);
  785.       exit_stat = 1;
  786.       return;
  787.     }
  788.     exit_stat = 0;
  789.  
  790. #ifdef TOS
  791.  
  792.     Fattrib(ofname, 1, Fattrib(ifname, 0, 0));
  793.  
  794. #else
  795.     mode = statbuf.st_mode & 07777;
  796.     if (chmod(ofname, mode))    /* Copy modes */
  797.       perror(ofname);
  798. #endif
  799. #ifndef DOS
  800.   /* Copy ownership */
  801.     (void) chown(ofname, (int) statbuf.st_uid, (int) statbuf.st_gid);
  802. #endif
  803.  
  804. #ifdef __TURBOC__
  805.     getftime(fileno(stdin), &utimbuf);
  806.     freopen(ofname, "rb", stdout);
  807.     setftime(fileno(stdout), &utimbuf);
  808.     (void) fclose(stdout);
  809. #else
  810. #ifdef UTIMES
  811.     timep[0].tv_sec = statbuf.st_atime;
  812.     timep[1].tv_sec = statbuf.st_mtime;
  813.     timep[0].tv_usec = timep[1].tv_usec = 0;
  814.     (void) utimes(ofname, timep);
  815. #else
  816.     timep.actime = statbuf.st_atime;
  817.     timep.modtime = statbuf.st_mtime;
  818.  
  819. #if defined(__m88k__)
  820.     timep.acusec = statbuf.st_ausec;    /* pa@verano */
  821.     timep.modusec = statbuf.st_musec;
  822. #endif                /* !m88k */
  823.  
  824.   /* Update last accessed and modified times */
  825.     (void) utime(ofname, &timep);
  826. #endif                /* UTIMES */
  827. #endif                /* __TURBOC__ */
  828.     if (unlink(ifname))        /* Remove input file */
  829.       perror(ifname);
  830.     if (quiet != 1)
  831.       fprintf(stderr, " -- replaced with %s\n", ofname);
  832.     return;            /* Successful return */
  833.   }
  834.  
  835.  /* Unsuccessful return -- one of the tests failed */
  836.   if (unlink(ofname))
  837.     perror(ofname);
  838. }
  839. /* Checks status of a file, returns 0 if the file may be frozen,
  840.     or 1 otherwise; assigns this value to exit_stat
  841. */
  842. int 
  843. checkstat(ifname)
  844. char   *ifname;
  845. {
  846.   if (stat(ifname, &statbuf)) {
  847.     perror(ifname);
  848.     return exit_stat = 1;
  849.   }
  850.  /* Do NOT try to freeze /dev/null or /dev/tty...   */
  851.  /* but you may freeze or melt everything to stdout */
  852.  
  853. #ifndef DOS
  854.   if (!topipe) {
  855.     if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
  856.       fprintf(stderr, "%s: ", ifname);
  857.       fprintf(stderr, " not a regular file -- unchanged\n");
  858.       return exit_stat = 1;
  859.  
  860.     } else if (statbuf.st_nlink > 1) {
  861.       fprintf(stderr, "%s: ", ifname);
  862.       fprintf(stderr, " has %d other links -- unchanged\n",
  863.     statbuf.st_nlink - 1);
  864.       return exit_stat = 1;
  865.     }
  866.   }
  867. #endif                /* MSDOS */
  868.  
  869.   file_length = statbuf.st_size;
  870.  
  871.   indc_threshold = file_length / 100;
  872.   if (indc_threshold < 4096)
  873.     indc_threshold = 4096;
  874.   if (do_melt)
  875.     indc_threshold *= 3;
  876.  
  877.   return exit_stat = 0;
  878. }
  879. #ifndef DOS
  880. /*
  881.  * This routine returns 1 if we are running in the foreground and stderr
  882.  * is a tty. (as in compress(1))
  883.  */
  884. int 
  885. foreground()
  886. {
  887.   if (bgnd_flag != SIG_DFL)    /* background? */
  888.     return (0);
  889.   else {            /* foreground */
  890.     if (isatty(2)) {        /* and stderr is a tty */
  891.       return (1);
  892.     } else {
  893.       return (0);
  894.     }
  895.   }
  896. }
  897. #endif
  898.  
  899. /* Exception handler (SIGINT) */
  900.  
  901. RETSIGTYPE
  902. onintr()
  903. {
  904.   if (!precious) {        /* topipe == 1 implies precious == 1 */
  905.     (void) fclose(stdout);
  906.     (void) unlink(ofname);
  907.   }
  908.   exit(1);
  909. }
  910. /* Exception handler (SIGSEGV) */
  911.  
  912. RETSIGTYPE
  913. oops()
  914. {                /* file is corrupt or internal error */
  915.   (void) fflush(stdout);
  916.   fprintf(stderr, "Segmentation violation occured (this shouldn't happen)\n");
  917.   exit(1);
  918. }
  919. /* Prints version and compilation options */
  920.  
  921. void 
  922. version()
  923. {
  924.   fprintf(stderr, ident, PATCHLEVEL, PATCHDATE);
  925.   fprintf(stderr, "LZSS 8192/256 + Huffman coding\nOptions: ");
  926. #ifdef COMPAT
  927.   fprintf(stderr, "compatible with vers. 1.0, ");
  928. #endif
  929. #ifdef DEBUG
  930.   fprintf(stderr, "DEBUG, ");
  931. #endif
  932. #if defined(SMALL) || defined(TINY)
  933.   fprintf(stderr, "SMALL, ");
  934. #endif
  935. #ifdef ALLOW_MISALIGN
  936.   fprintf(stderr, "ALLOW_MISALIGN, ");
  937. #endif
  938. #ifdef GATHER_STAT
  939.   fprintf(stderr, "GATHER_STAT, ");
  940. #endif
  941.   fprintf(stderr, "HASH: %d bits\n", BITS);
  942.   fprintf(stderr, "Static Huffman table: %d %d %d %d %d %d %d %d\n",
  943.     Table2[1], Table2[2], Table2[3], Table2[4],
  944.     Table2[5], Table2[6], Table2[7], Table2[8]);
  945. #ifdef DOS
  946.   fprintf(stderr, "Default melted mode: %s\n",
  947.     image == O_BINARY ? "binary" : "text");
  948. #endif
  949.   exit(0);
  950. }
  951. /* Deals with static Huffman table parameters.
  952.     Input: command line option w/o leading `+' or `--'.
  953.     Output: fills the array `Table2' if OK, exit(1) otherwise.
  954. */
  955.  
  956. void 
  957. tune_table(type)
  958. char   *type;
  959. {
  960.   extern char *defread();
  961.   register char *s = defread(type), *t;
  962.   static int v[8];
  963.   int     i, is_list = 0;
  964.   if (!s) {
  965.   /* try to consider 'type' as a list of values: n1,n2, ... */
  966.     if (strrchr(type, ','))
  967.       is_list = 1;
  968.     else {
  969.       fprintf(stderr, "\"%s\" - no such file type\n", type);
  970.       exit(1);
  971.     }
  972.     if (sscanf(type, "%d,%d,%d,%d,%d,%d,%d,%d",
  973.     v, v + 1, v + 2, v + 3, v + 4, v + 5, v + 6, v + 7) != 8) {
  974.       fprintf(stderr,
  975.     "%s - a list of 8 numbers expected\n", type);
  976.       exit(1);
  977.     }
  978.   }
  979.  
  980.   /* both space-separated and comma-separated lists are allowed */
  981.  
  982.   if (!is_list && (!(t = strrchr(s, '=')) ||
  983.       sscanf(++t, "%d %d %d %d %d %d %d %d",
  984.     v, v + 1, v + 2, v + 3, v + 4, v + 5, v + 6, v + 7) != 8 &&
  985.       sscanf(t, "%d,%d,%d,%d,%d,%d,%d,%d",
  986.     v, v + 1, v + 2, v + 3, v + 4, v + 5, v + 6, v + 7) != 8)) {
  987.     fprintf(stderr,
  988.       "\"%s\" - invalid entry\n", type);
  989.     exit(1);
  990.   }
  991.   for (i = 0; i < 8; i++)
  992.     Table2[i + 1] = v[i];
  993.   if (quiet < 0) {
  994.     if (!is_list) {
  995.       t = s;
  996.     /* make full word */
  997.       while (*s != '=' && *s != ' ' && *s != '\t')
  998.     s++;
  999.       *s = '\0';
  1000.     } else
  1001.       t = "";
  1002.     fprintf(stderr, "Using \"%s%s\" type\n", type, t);
  1003.   }
  1004. }
  1005. #ifdef DOS
  1006.  
  1007. /* MSDOS typically has ':' and '\\' separators, but some command
  1008.   processors (and the int 21h function handler) support '/' too.
  1009.   Find the last of these.
  1010. */
  1011.  
  1012. char   *
  1013. last_sep(s)
  1014. register char *s;
  1015. {
  1016.   char   *p;
  1017.   for (p = NULL; *s; s++)
  1018.     if (*s == '/' || *s == '\\' || *s == ':')
  1019.       p = s;
  1020.   return (p);
  1021. }
  1022. #endif                /* DOS */
  1023.